<template>
  <div :class="[
    type === 'textarea' ? 'el-textarea' : 'el-input',
    inputSize ? 'el-input--' + inputSize : '',
    {
      'is-disabled': inputDisabled,
      'is-exceed': inputExceed,
      'el-input-group': $slots.prepend || $slots.append,
      'el-input-group--append': $slots.append,
      'el-input-group--prepend': $slots.prepend,
      'el-input--prefix': $slots.prefix || prefixIcon,
      'el-input--suffix': $slots.suffix || suffixIcon || clearable || showPassword
    }
    ]"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false"
  >
    <template v-if="type !== 'textarea'">
      <!-- 前置元素 -->
      <div class="el-input-group__prepend" v-if="$slots.prepend">
        <slot name="prepend"></slot>
      </div>
      <input
        :tabindex="tabindex"
        v-if="type !== 'textarea'"
        class="el-input__inner"
        v-bind="$attrs"
        :type="showPassword ? (passwordVisible ? 'text': 'password') : type"
        :disabled="inputDisabled"
        :readonly="readonly"
        :autocomplete="autoComplete || autocomplete"
        ref="input"
        @compositionstart="handleCompositionStart"
        @compositionupdate="handleCompositionUpdate"
        @compositionend="handleCompositionEnd"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur"
        @change="handleChange"
        :aria-label="label"
      >
      <!-- 前置内容 -->
      <span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
        <slot name="prefix"></slot>
        <i class="el-input__icon"
           v-if="prefixIcon"
           :class="prefixIcon">
        </i>
      </span>
      <!-- 后置内容 -->
      <span
        class="el-input__suffix"
        v-if="getSuffixVisible()">
        <span class="el-input__suffix-inner">
          <template v-if="!showClear || !showPwdVisible || !isWordLimitVisible">
            <slot name="suffix"></slot>
            <i class="el-input__icon"
              v-if="suffixIcon"
              :class="suffixIcon">
            </i>
          </template>
          <i v-if="showClear"
            class="el-input__icon el-icon-circle-close el-input__clear"
            @mousedown.prevent
            @click="clear"
          ></i>
          <i v-if="showPwdVisible"
            class="el-input__icon el-icon-view el-input__clear"
            @click="handlePasswordVisible"
          ></i>
          <span v-if="isWordLimitVisible" class="el-input__count">
            <span class="el-input__count-inner">
              {{ textLength }}/{{ upperLimit }}
            </span>
          </span>
        </span>
        <i class="el-input__icon"
          v-if="validateState"
          :class="['el-input__validateIcon', validateIcon]">
        </i>
      </span>
      <!-- 后置元素 -->
      <div class="el-input-group__append" v-if="$slots.append">
        <slot name="append"></slot>
      </div>
    </template>
    <textarea
      v-else
      :tabindex="tabindex"
      class="el-textarea__inner"
      @compositionstart="handleCompositionStart"
      @compositionupdate="handleCompositionUpdate"
      @compositionend="handleCompositionEnd"
      @input="handleInput"
      ref="textarea"
      v-bind="$attrs"
      :disabled="inputDisabled"
      :readonly="readonly"
      :autocomplete="autoComplete || autocomplete"
      :style="textareaStyle"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="handleChange"
      :aria-label="label"
    >
    </textarea>
    <span v-if="isWordLimitVisible && type === 'textarea'" class="el-input__count">{{ textLength }}/{{ upperLimit }}</span>
  </div>
</template>
<script>
  import emitter from '@/utils/mixins/emitter';
  import Migrating from '@/utils/mixins/migrating';
  import calcTextareaHeight from './calcTextareaHeight';
  import merge from '@/utils/merge';
  import {isKorean} from '@/utils/shared';

  export default {
    name: 'ElInput',

    componentName: 'ElInput',

    mixins: [emitter, Migrating],

    inheritAttrs: false,

    inject: {
      elForm: {
        default: ''
      },
      elFormItem: {
        default: ''
      }
    },

    data() {
      return {
        textareaCalcStyle: {},
        hovering: false,
        focused: false,
        isComposing: false,
        passwordVisible: false
      };
    },

    props: {
      value: [String, Number],
      size: String,
      resize: String,
      form: String,
      disabled: Boolean,
      readonly: Boolean,
      type: {
        type: String,
        default: 'text'
      },
      autosize: {
        type: [Boolean, Object],
        default: false
      },
      autocomplete: {
        type: String,
        default: 'off'
      },
      /** @Deprecated in next major version */
      autoComplete: {
        type: String,
        validator(val) {
          process.env.NODE_ENV !== 'production' &&
            console.warn('[Element Warn][Input]\'auto-complete\' property will be deprecated in next major version. please use \'autocomplete\' instead.');
          return true;
        }
      },
      validateEvent: {
        type: Boolean,
        default: true
      },
      suffixIcon: String,
      prefixIcon: String,
      label: String,
      clearable: {
        type: Boolean,
        default: false
      },
      showPassword: {
        type: Boolean,
        default: false
      },
      showWordLimit: {
        type: Boolean,
        default: false
      },
      tabindex: String
    },

    computed: {
      _elFormItemSize() {
        return (this.elFormItem || {}).elFormItemSize;
      },
      validateState() {
        return this.elFormItem ? this.elFormItem.validateState : '';
      },
      needStatusIcon() {
        return this.elForm ? this.elForm.statusIcon : false;
      },
      validateIcon() {
        return {
          validating: 'el-icon-loading',
          success: 'el-icon-circle-check',
          error: 'el-icon-circle-close'
        }[this.validateState];
      },
      textareaStyle() {
        return merge({}, this.textareaCalcStyle, { resize: this.resize });
      },
      inputSize() {
        return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
      },
      inputDisabled() {
        return this.disabled || (this.elForm || {}).disabled;
      },
      nativeInputValue() {
        return this.value === null || this.value === undefined ? '' : String(this.value);
      },
      showClear() {
        return this.clearable &&
          !this.inputDisabled &&
          !this.readonly &&
          this.nativeInputValue &&
          (this.focused || this.hovering);
      },
      showPwdVisible() {
        return this.showPassword &&
          !this.inputDisabled &&
          !this.readonly &&
          (!!this.nativeInputValue || this.focused);
      },
      isWordLimitVisible() {
        return this.showWordLimit &&
          this.$attrs.maxlength &&
          (this.type === 'text' || this.type === 'textarea') &&
          !this.inputDisabled &&
          !this.readonly &&
          !this.showPassword;
      },
      upperLimit() {
        return this.$attrs.maxlength;
      },
      textLength() {
        if (typeof this.value === 'number') {
          return String(this.value).length;
        }

        return (this.value || '').length;
      },
      inputExceed() {
        // show exceed style if length of initial value greater then maxlength
        return this.isWordLimitVisible &&
          (this.textLength > this.upperLimit);
      }
    },

    watch: {
      value(val) {
        this.$nextTick(this.resizeTextarea);
        if (this.validateEvent) {
          this.dispatch('ElFormItem', 'el.form.change', [val]);
        }
      },
      // native input value is set explicitly
      // do not use v-model / :value in template
      // see: https://github.com/ElemeFE/element/issues/14521
      nativeInputValue() {
        this.setNativeInputValue();
      },
      // when change between <input> and <textarea>,
      // update DOM dependent value and styles
      // https://github.com/ElemeFE/element/issues/14857
      type() {
        this.$nextTick(() => {
          this.setNativeInputValue();
          this.resizeTextarea();
          this.updateIconOffset();
        });
      }
    },

    methods: {
      focus() {
        this.getInput().focus();
      },
      blur() {
        this.getInput().blur();
      },
      getMigratingConfig() {
        return {
          props: {
            'icon': 'icon is removed, use suffix-icon / prefix-icon instead.',
            'on-icon-click': 'on-icon-click is removed.'
          },
          events: {
            'click': 'click is removed.'
          }
        };
      },
      handleBlur(event) {
        this.focused = false;
        this.$emit('blur', event);
        if (this.validateEvent) {
          this.dispatch('ElFormItem', 'el.form.blur', [this.value]);
        }
      },
      select() {
        this.getInput().select();
      },
      resizeTextarea() {
        if (this.$isServer) return;
        const { autosize, type } = this;
        if (type !== 'textarea') return;
        if (!autosize) {
          this.textareaCalcStyle = {
            minHeight: calcTextareaHeight(this.$refs.textarea).minHeight
          };
          return;
        }
        const minRows = autosize.minRows;
        const maxRows = autosize.maxRows;

        this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
      },
      setNativeInputValue() {
        const input = this.getInput();
        if (!input) return;
        if (input.value === this.nativeInputValue) return;
        input.value = this.nativeInputValue;
      },
      handleFocus(event) {
        this.focused = true;
        this.$emit('focus', event);
      },
      handleCompositionStart() {
        this.isComposing = true;
      },
      handleCompositionUpdate(event) {
        const text = event.target.value;
        const lastCharacter = text[text.length - 1] || '';
        this.isComposing = !isKorean(lastCharacter);
      },
      handleCompositionEnd(event) {
        if (this.isComposing) {
          this.isComposing = false;
          this.handleInput(event);
        }
      },
      handleInput(event) {
        // should not emit input during composition
        // see: https://github.com/ElemeFE/element/issues/10516
        if (this.isComposing) return;

        // hack for https://github.com/ElemeFE/element/issues/8548
        // should remove the following line when we don't support IE
        if (event.target.value === this.nativeInputValue) return;

        this.$emit('input', event.target.value);

        // ensure native input value is controlled
        // see: https://github.com/ElemeFE/element/issues/12850
        this.$nextTick(this.setNativeInputValue);
      },
      handleChange(event) {
        this.$emit('change', event.target.value);
      },
      calcIconOffset(place) {
        let elList = [].slice.call(this.$el.querySelectorAll(`.el-input__${place}`) || []);
        if (!elList.length) return;
        let el = null;
        for (let i = 0; i < elList.length; i++) {
          if (elList[i].parentNode === this.$el) {
            el = elList[i];
            break;
          }
        }
        if (!el) return;
        const pendantMap = {
          suffix: 'append',
          prefix: 'prepend'
        };

        const pendant = pendantMap[place];
        if (this.$slots[pendant]) {
          el.style.transform = `translateX(${place === 'suffix' ? '-' : ''}${this.$el.querySelector(`.el-input-group__${pendant}`).offsetWidth}px)`;
        } else {
          el.removeAttribute('style');
        }
      },
      updateIconOffset() {
        this.calcIconOffset('prefix');
        this.calcIconOffset('suffix');
      },
      clear() {
        this.$emit('input', '');
        this.$emit('change', '');
        this.$emit('clear');
      },
      handlePasswordVisible() {
        this.passwordVisible = !this.passwordVisible;
        this.focus();
      },
      getInput() {
        return this.$refs.input || this.$refs.textarea;
      },
      getSuffixVisible() {
        return this.$slots.suffix ||
          this.suffixIcon ||
          this.showClear ||
          this.showPassword ||
          this.isWordLimitVisible ||
          (this.validateState && this.needStatusIcon);
      }
    },

    created() {
      this.$on('inputSelect', this.select);
    },

    mounted() {
      this.setNativeInputValue();
      this.resizeTextarea();
      this.updateIconOffset();
    },

    updated() {
      this.$nextTick(this.updateIconOffset);
    }
  };
</script>

<style lang="scss">
//colors
$color_shuttle_gray_approx: #606266;
$white: #fff;
$color_mischka_approx: #dcdfe6;
$color_ghost_approx: #c0c4cc;
$color_dodger_blue_approx: #409eff;
$color_manatee_approx: #909399;
$color_athens_gray_approx: #f5f7fa;
$color_bittersweet_approx: #f56c6c;
$color_heather_approx: #b4bccc;

//@extend-elements
//original selectors
//.el-input__prefix, .el-input__suffix
%extend_1 {
  position: absolute;
  top: 0;
  text-align: center;
  height: 100%;
  color: $color_ghost_approx;
}

//original selectors
//.el-input-group__append, .el-input-group__prepend
%extend_2 {
  background-color: $color_athens_gray_approx;
  color: $color_manatee_approx;
  vertical-align: middle;
  display: table-cell;
  position: relative;
  border: 1px solid $color_mischka_approx;
  //Instead of the line below you could use @include border-radius($radius, $vertical-radius)
  border-radius: 4px;
  padding: 0 20px;
  width: 1px;
  white-space: nowrap;
}

//original selectors
//.el-input-group__append button.el-button, .el-input-group__append div.el-select .el-input__inner, .el-input-group__append div.el-select:hover .el-input__inner, .el-input-group__prepend button.el-button, .el-input-group__prepend div.el-select .el-input__inner, .el-input-group__prepend div.el-select:hover .el-input__inner
%extend_3 {
  border-color: transparent;
  background-color: transparent;
  color: inherit;
  border-top: 0;
  border-bottom: 0;
}


.el-textarea {
  position: relative;
  display: inline-block;
  width: 100%;
  vertical-align: bottom;
  font-size: 14px;
  .el-input__count {
    color: $color_manatee_approx;
    background: $white;
    position: absolute;
    font-size: 12px;
    bottom: 5px;
    right: 10px;
  }
  &.is-disabled {
    .el-textarea__inner {
      background-color: $color_athens_gray_approx;
      border-color: $color_athens_gray_approx;
      color: $color_ghost_approx;
      cursor: not-allowed;
      &:-ms-input-placeholder {
        color: $color_ghost_approx;
      }
    }
    .el-textarea__inner::-webkit-input-placeholder {
      color: $color_ghost_approx;
    }
    .el-textarea__inner::-ms-input-placeholder {
      color: $color_ghost_approx;
    }
    .el-textarea__inner::placeholder {
      color: $color_ghost_approx;
    }
  }
  &.is-exceed {
    .el-textarea__inner {
      border-color: $color_bittersweet_approx;
    }
    .el-input__count {
      color: $color_bittersweet_approx;
    }
  }
}
.el-textarea__inner {
  display: block;
  resize: vertical;
  padding: 5px 15px;
  line-height: 1.5;
  //Instead of the line below you could use @include box-sizing($bs)
  box-sizing: border-box;
  width: 100%;
  font-size: inherit;
  color: $color_shuttle_gray_approx;
  background-color: $white;
  background-image: none;
  border: 1px solid $color_mischka_approx;
  //Instead of the line below you could use @include border-radius($radius, $vertical-radius)
  border-radius: 4px;
  //Instead of the line below you could use @include transition($transition-1, $transition-2, $transition-3, $transition-4, $transition-5, $transition-6, $transition-7, $transition-8, $transition-9, $transition-10)
  transition: border-color .2s cubic-bezier(.645,.045,.355,1);
  &:-ms-input-placeholder {
    color: $color_ghost_approx;
  }
  &:hover {
    border-color: $color_ghost_approx;
  }
  &:focus {
    outline: 0;
    border-color: $color_dodger_blue_approx;
  }
}
.el-textarea__inner::-webkit-input-placeholder {
  color: $color_ghost_approx;
}
.el-textarea__inner::-ms-input-placeholder {
  color: $color_ghost_approx;
}
.el-textarea__inner::placeholder {
  color: $color_ghost_approx;
}
.el-input {
  position: relative;
  font-size: 14px;
  display: inline-block;
  width: 100%;
  .el-input__clear {
    color: $color_ghost_approx;
    font-size: 14px;
    cursor: pointer;
    //Instead of the line below you could use @include transition($transition-1, $transition-2, $transition-3, $transition-4, $transition-5, $transition-6, $transition-7, $transition-8, $transition-9, $transition-10)
    transition: color .2s cubic-bezier(.645,.045,.355,1);
    &:hover {
      color: $color_manatee_approx;
    }
  }
  .el-input__count {
    height: 100%;
    display: inline-flex;
    align-items: center;
    color: $color_manatee_approx;
    font-size: 12px;
    .el-input__count-inner {
      background: $white;
      line-height: initial;
      display: inline-block;
      padding: 0 5px;
    }
  }
  &.is-disabled {
    .el-input__inner {
      background-color: $color_athens_gray_approx;
      border-color: $color_athens_gray_approx;
      color: $color_ghost_approx;
      cursor: not-allowed;
      &:-ms-input-placeholder {
        color: $color_ghost_approx;
      }
    }
    .el-input__inner::-webkit-input-placeholder {
      color: $color_ghost_approx;
    }
    .el-input__inner::-ms-input-placeholder {
      color: $color_ghost_approx;
    }
    .el-input__inner::placeholder {
      color: $color_ghost_approx;
    }
    .el-input__icon {
      cursor: not-allowed;
    }
  }
  &.is-exceed {
    .el-input__inner {
      border-color: $color_bittersweet_approx;
    }
    .el-input__suffix .el-input__count {
      color: $color_bittersweet_approx;
    }
  }
  &.is-active .el-input__inner {
    border-color: $color_dodger_blue_approx;
    outline: 0;
  }
}
.el-input::-webkit-scrollbar {
  z-index: 11;
  width: 6px;
  &:horizontal {
    height: 6px;
  }
}
.el-input::-webkit-scrollbar-thumb {
  //Instead of the line below you could use @include border-radius($radius, $vertical-radius)
  border-radius: 5px;
  width: 6px;
  background: $color_heather_approx;
}
.el-input::-webkit-scrollbar-corner {
  background: $white;
}
.el-input::-webkit-scrollbar-track {
  background: $white;
}
.el-input::-webkit-scrollbar-track-piece {
  background: $white;
  width: 6px;
}
.el-input__inner {
  background-color: $white;
  background-image: none;
  //Instead of the line below you could use @include border-radius($radius, $vertical-radius)
  border-radius: 4px;
  border: 1px solid $color_mischka_approx;
  //Instead of the line below you could use @include box-sizing($bs)
  box-sizing: border-box;
  color: $color_shuttle_gray_approx;
  display: inline-block;
  font-size: inherit;
  height: 40px;
  line-height: 40px;
  outline: 0;
  padding: 0 15px;
  //Instead of the line below you could use @include transition($transition-1, $transition-2, $transition-3, $transition-4, $transition-5, $transition-6, $transition-7, $transition-8, $transition-9, $transition-10)
  transition: border-color .2s cubic-bezier(.645,.045,.355,1);
  width: 100%;
  &:-ms-input-placeholder {
    color: $color_ghost_approx;
  }
  &:hover {
    border-color: $color_ghost_approx;
  }
  &:focus {
    border-color: $color_dodger_blue_approx;
    outline: 0;
  }
}
.el-input__prefix {
  @extend %extend_1;
  left: 5px;
  //Instead of the line below you could use @include transition($transition-1, $transition-2, $transition-3, $transition-4, $transition-5, $transition-6, $transition-7, $transition-8, $transition-9, $transition-10)
  transition: all .3s;
}
.el-input__suffix {
  @extend %extend_1;
  right: 5px;
  //Instead of the line below you could use @include transition($transition-1, $transition-2, $transition-3, $transition-4, $transition-5, $transition-6, $transition-7, $transition-8, $transition-9, $transition-10)
  transition: all .3s;
  pointer-events: none;
}
.el-input__inner::-webkit-input-placeholder {
  color: $color_ghost_approx;
}
.el-input__inner::-ms-input-placeholder {
  color: $color_ghost_approx;
}
.el-input__inner::placeholder {
  color: $color_ghost_approx;
}
.el-input__suffix-inner {
  pointer-events: all;
}
.el-input__icon {
  height: 100%;
  width: 25px;
  text-align: center;
  //Instead of the line below you could use @include transition($transition-1, $transition-2, $transition-3, $transition-4, $transition-5, $transition-6, $transition-7, $transition-8, $transition-9, $transition-10)
  transition: all .3s;
  line-height: 40px;
  &:after {
    content: '';
    height: 100%;
    width: 0;
    display: inline-block;
    vertical-align: middle;
  }
}
.el-input__validateIcon {
  pointer-events: none;
}
.el-input--medium {
  font-size: 14px;
  .el-input__inner {
    height: 36px;
    line-height: 36px;
  }
  .el-input__icon {
    line-height: 36px;
  }
}
.el-input--small {
  font-size: 13px;
  .el-input__inner {
    height: 32px;
    line-height: 32px;
  }
  .el-input__icon {
    line-height: 32px;
  }
}
.el-input--mini {
  font-size: 12px;
  .el-input__inner {
    height: 28px;
    line-height: 28px;
  }
  .el-input__icon {
    line-height: 28px;
  }
}
.el-input-group {
  line-height: normal;
  display: inline-table;
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  > .el-input__inner {
    vertical-align: middle;
    display: table-cell;
  }
}
.el-input-group__append {
  @extend %extend_2;
  //Instead of the line below you could use @include border-top-left-radius($radius)
  border-top-left-radius: 0;
  //Instead of the line below you could use @include border-bottom-left-radius($radius)
  border-bottom-left-radius: 0;
  border-left: 0;
  &:focus {
    outline: 0;
  }
  .el-button {
    display: inline-block;
    margin: -10px -20px;
    font-size: inherit;
  }
  .el-select {
    display: inline-block;
    margin: -10px -20px;
  }
  .el-input {
    font-size: inherit;
  }
  button.el-button {
    @extend %extend_3;
  }
  div.el-select {
    .el-input__inner {
      @extend %extend_3;
    }
    &:hover .el-input__inner {
      @extend %extend_3;
    }
  }
}
.el-input-group__prepend {
  @extend %extend_2;
  //Instead of the line below you could use @include border-top-right-radius($radius)
  border-top-right-radius: 0;
  //Instead of the line below you could use @include border-bottom-right-radius($radius)
  border-bottom-right-radius: 0;
  border-right: 0;
  &:focus {
    outline: 0;
  }
  .el-button {
    display: inline-block;
    margin: -10px -20px;
    font-size: inherit;
  }
  .el-select {
    display: inline-block;
    margin: -10px -20px;
  }
  .el-input {
    font-size: inherit;
  }
  button.el-button {
    @extend %extend_3;
  }
  div.el-select {
    .el-input__inner {
      @extend %extend_3;
    }
    &:hover .el-input__inner {
      @extend %extend_3;
    }
  }
}
.el-input-group--prepend {
  .el-input__inner {
    //Instead of the line below you could use @include border-top-left-radius($radius)
    border-top-left-radius: 0;
    //Instead of the line below you could use @include border-bottom-left-radius($radius)
    border-bottom-left-radius: 0;
  }
  .el-select .el-input.is-focus .el-input__inner {
    border-color: transparent;
  }
}
.el-input-group--append {
  .el-input__inner {
    //Instead of the line below you could use @include border-top-right-radius($radius)
    border-top-right-radius: 0;
    //Instead of the line below you could use @include border-bottom-right-radius($radius)
    border-bottom-right-radius: 0;
  }
  .el-select .el-input.is-focus .el-input__inner {
    border-color: transparent;
  }
}
.el-input__inner::-ms-clear {
  display: none;
  width: 0;
  height: 0;
}
.el-input--suffix .el-input__inner {
  padding-right: 30px;
}
.el-input--prefix .el-input__inner {
  padding-left: 30px;
}
</style>
